<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { LogicalSize, getCurrentWindow } from '@tauri-apps/api/window'
import { invoke } from '@tauri-apps/api/core';
import { listen, once } from '@tauri-apps/api/event';
import LoadingIndicator from './components/LoadingIndicator.vue';
import ProgressIndicator from './components/ProgressIndicator.vue';
import { message, open } from '@tauri-apps/plugin-dialog';

enum OTA_STATE {
  OTA_STATE_IDLE,
  OTA_STATE_DOWNLOAD_FILE,
  OTA_STATE_DOWNLOAD_FILE_ERROR,
  OTA_STATE_NO_UPDATE_FILE,
  OTA_STATE_VERIFY_FILE,
  OTA_STATE_VERIFY_FILE_ERROR,
  OTA_STATE_CHECK_USB,
  OTA_STATE_CHECK_USB_ERROR,
  OTA_STATE_WAIT_VERIFY_DEVICE,
  OTA_STATE_VERIFY_DEVICE,
  OTA_STATE_VERIFY_DEVICE_ERROR,
  OTA_STATE_READY_TO_UPDATE,
  OTA_STATE_UPDATE,
  OTA_STATE_UPDATE_ERROR,
  OTA_STATE_DONE
};

const url = 'https://gw.test.waixingkeji.net/alien-smart-audio/api/ota/index?type=1';
const isUsbConnected = ref(false); // This will need to be reactive based on actual USB state
const isReady = ref(false); // This will control button state
const dialogRef = ref<HTMLDivElement | null>(null);
const isUpdating = ref(false);
const progress = ref(0);
const showProgress = ref(false);
const loadingText = ref('');
const isLoading = ref(false);
const showUsbWarning = ref(false);
const warningText = ref("");
const buttonFobidden = ref(false);
const errorInfo = ref("");
const otaPath = ref("");
const debugMode = ref(false);

let warningTimer: any = null;
const curState = ref(OTA_STATE.OTA_STATE_IDLE);

let driver = '';
let deviceName = "";
let crc32 = 0;

const showWarnning = (text: string) => {
  if (warningTimer !== null) {
    clearTimeout(warningTimer);
  }
  showUsbWarning.value = true;
  warningText.value = text;
  warningTimer = setTimeout(() => {
    showUsbWarning.value = false;
    warningTimer = null;
  }, 3000);
};

const verifyDevice = () => {
  errorInfo.value = "";
  isLoading.value = true;
  loadingText.value = 'Validating device...';
  curState.value = OTA_STATE.OTA_STATE_VERIFY_DEVICE;

  console.log('Verify device', deviceName, driver);
  
  once('ota_verify_device_success', (event) => {
      console.log('OTA validate success', event.payload);
      curState.value = OTA_STATE.OTA_STATE_READY_TO_UPDATE;
      isLoading.value = false;
      isReady.value = true;
  })

  once('ota_verify_device_error', async (event: any) => {
    console.log('OTA validate error', event.payload);

    errorInfo.value = `error: ${event.payload.error} error_code: ${event.payload.error_code}`;
    curState.value = OTA_STATE.OTA_STATE_VERIFY_DEVICE_ERROR;
    isLoading.value = false;

    if (event.payload.error_code == -3) {
      await message("update file is not matched", {title: "Error", kind: "error"});
    }else {
      await message("Make sure right device to be updated", {title: "Error", kind: "error"});
    }
  });

  invoke('ota_verify_device_and_ufw', {devName: deviceName, ch: driver}).then((status) => {
    console.log('OTA validate starting...', status);
  }).catch(async (error) => {
    curState.value = OTA_STATE.OTA_STATE_VERIFY_DEVICE_ERROR;
    isLoading.value = false;
    console.error('Error validating OTA', error);
    await message("Make sure right device to be updated", {title: "Error", kind: "error"});
  });
}


const checkingUSB = () => {
  errorInfo.value = "";
  curState.value = OTA_STATE.OTA_STATE_CHECK_USB;
  once('usb_connect_status', (event: any) => {
      console.log('USB connect status', event.payload);
      isUsbConnected.value = true;
      driver = event.payload.drv;
      deviceName = event.payload.name;

      console.log('Driver:', driver, 'Device:', deviceName);
      setTimeout(() => {
        verifyDevice();
      }, 500);
  });

  once('usb_connect_status_error', (event: any) => {
    console.error('USB connect status error', event.payload);
    errorInfo.value = `error: ${event.payload.error} error_code: ${event.payload.error_code}`;
    isUsbConnected.value = false;
    curState.value = OTA_STATE.OTA_STATE_CHECK_USB_ERROR;
    showWarnning("Please connect USB to update");
  }); 
  invoke('ota_get_usb_info').then((result) => {
    console.log('USB connected status', result);   
  }).catch((error) => {
    curState.value = OTA_STATE.OTA_STATE_CHECK_USB_ERROR;
    console.error('Error checking USB status', error);
    isUsbConnected.value = false;
    showWarnning("Please connect USB to update");
  })
};

const verifyDownloadFile = () => {
  isLoading.value = true;
  curState.value = OTA_STATE.OTA_STATE_VERIFY_FILE;
  loadingText.value = 'Validating update data...';

  invoke('ota_verify_file', {crc32, filePath: otaPath.value}).then((status) => {
    console.log('OTA verify success', status);
    isLoading.value = false;
    setTimeout(() => {
      checkingUSB();
    }, 100);
  }).catch((error) => {
    curState.value = OTA_STATE.OTA_STATE_VERIFY_FILE_ERROR
    console.error('Error validating OTA', error);
    isLoading.value = false;
    showWarnning("Invalid update file");
    setTimeout(() => {
      getOtaFile();
    })
  });
}


const getOtaFile = () => {
  console.log('Refresh button clicked', curState.value);
  errorInfo.value = "";
  isLoading.value = true;
  loadingText.value = 'Getting update data...';
  curState.value = OTA_STATE.OTA_STATE_DOWNLOAD_FILE;

  invoke('ota_get_file', {url: url}).then((result) => {
    if (result == "success") {
      console.log('Download complete');
      isLoading.value = false;
      setTimeout(() => {
        checkingUSB();
      }, 100)
    }else if (result == "pending") {
      once('ota_download_success', (event: any) => {
        console.log('OTA download success', event.payload);
        if (event.payload == 0) {
          curState.value = OTA_STATE.OTA_STATE_NO_UPDATE_FILE;
          isLoading.value = false;
          return;
        }
        isLoading.value = false;
        crc32 = event.payload.crc32;
        otaPath.value = event.payload.path;
        verifyDownloadFile();
      });

      once('ota_download_error', async (event) => {
        console.error('OTA download error', event.payload);
        errorInfo.value = event.payload as string;
        curState.value = OTA_STATE.OTA_STATE_DOWNLOAD_FILE_ERROR;
        isLoading.value = false;
        await message("Make sure network is ok, press continue to try again", {title: "Error", kind: "error"});
      });
    }
  }).catch((error) => {
    curState.value = OTA_STATE.OTA_STATE_DOWNLOAD_FILE_ERROR;
    console.error('Error downloading OTA', error);
    errorInfo.value = error as string;
    isLoading.value = false;
    showWarnning("Make sure network is ok, need to download update file");
  });
}

onMounted(async () => {
  if (dialogRef.value) {
    const width = dialogRef.value.offsetWidth + 40; // Add padding
    const height = dialogRef.value.offsetHeight + 60;

    console.log(`Setting window size to ${width}x${height}`);
    await getCurrentWindow().setSize(new LogicalSize(width, height));

    await getCurrentWindow().setResizable(false);

    await getCurrentWindow().center();
  }

  listen('upgrade_progress', (event) => {
    console.log(event);
    // Assuming event payload contains progress value
    progress.value = event.payload as number;
    if (!buttonFobidden.value) {
      buttonFobidden.value = true;
    }
  });

  invoke('ota_init').then((status) => {
    console.log('OTA init success', status);
    loadingText.value = "waiting to load or fetch update file";
  }).catch(async (error) => {
    console.error('Error initializing OTA', error);
    await message("Missing dll, press ok to close app", {title: "Error", kind: "error"});
    invoke('exit_app');
  }); 
});


const updateBin = async () => {
    console.log('Update button clicked', curState.value);
    errorInfo.value = ""
    
    if (curState.value == OTA_STATE.OTA_STATE_DOWNLOAD_FILE_ERROR ||
        curState.value == OTA_STATE.OTA_STATE_NO_UPDATE_FILE || 
        curState.value == OTA_STATE.OTA_STATE_VERIFY_FILE_ERROR) {
      showWarnning("Please reload ota file or refetch it from remote");
    }else if (curState.value == OTA_STATE.OTA_STATE_CHECK_USB_ERROR) {
      showWarnning("Please connect USB to update");
      checkingUSB();
    }else if (curState.value == OTA_STATE.OTA_STATE_VERIFY_DEVICE_ERROR) {
      showWarnning("Make sure right device to be updated");
      checkingUSB();
    }else if (curState.value == OTA_STATE.OTA_STATE_WAIT_VERIFY_DEVICE) {
      verifyDevice();
    }else if (curState.value == OTA_STATE.OTA_STATE_READY_TO_UPDATE ||
              curState.value == OTA_STATE.OTA_STATE_UPDATE_ERROR) {
      progress.value = 0;
      isUpdating.value = true;
      once('ota_update_success', async (event) => {
        console.log('OTA update success', event.payload);
        curState.value = OTA_STATE.OTA_STATE_DONE;
        isUpdating.value = false;
        progress.value = 100;
        showProgress.value = false;
        buttonFobidden.value = false;
        isReady.value = false;
        await message("Update success, please remove usb and restart device", {title: "Success", kind: "info"});
        setTimeout(() => {
          curState.value = OTA_STATE.OTA_STATE_WAIT_VERIFY_DEVICE;  
        }, 500)
      })

      once('ota_update_error', (event: any) => {
        console.error('OTA update error', event.payload);
        buttonFobidden.value = false;
        curState.value = OTA_STATE.OTA_STATE_UPDATE_ERROR;
        isUpdating.value = false;
        progress.value = 0;
        showProgress.value = false;
        showWarnning("Update failed, please try again");

        if (event.payload.error_code) {
          switch (event.payload.error_code) {
            case -9:
            case -23:
            case -5: {
              isReady.value = false;
              isLoading.value = true;
              setTimeout(() => {
                checkingUSB();
              }, 500);
            }
            break;
          }
        }
      })

      curState.value = OTA_STATE.OTA_STATE_UPDATE;
      invoke('ota_update_device', {ch: driver, name: deviceName}).then((status) => {
        console.log('OTA update success', status);
        showProgress.value = true;
      }).catch((error) => {
        buttonFobidden.value = false;
        console.error('OTA update error', error);
        curState.value = OTA_STATE.OTA_STATE_UPDATE_ERROR;
        isUpdating.value = false;
        progress.value = 0;
        showWarnning("Update failed, please try again");
      }); 
    }else if (curState.value == OTA_STATE.OTA_STATE_IDLE) {
      showWarnning("Please fetch or loading update file");
    }
  }

const loadOtaFile = async () => {
  errorInfo.value = "";
  try {
    const selected = await open({
      multiple: false,
      filters: [{
        name: 'All Files',
        extensions: ['ufw']
      }]
    });

    if (selected) {
      const filename = selected as string;
      console.log("filename is", filename);
      otaPath.value = filename;

      const crc32Result = await invoke('ota_calculate_crc32', { filename });
      console.log('CRC32:', crc32Result);

      setTimeout(() => {
        checkingUSB();
      }, 100);
    }
  } catch (error) {
    console.error('Error loading OTA file', error);
    message("Failed to load OTA file", { title: "Error", kind: "error" });
  }
}


const info_msg = computed((): string => {
  if (curState.value == OTA_STATE.OTA_STATE_IDLE) {
    return "Waiting to load or fetch update file";
  }else if (curState.value == OTA_STATE.OTA_STATE_DOWNLOAD_FILE ||
            curState.value == OTA_STATE.OTA_STATE_DOWNLOAD_FILE_ERROR) {
    return "Downloading update data...";
  }else if (curState.value == OTA_STATE.OTA_STATE_VERIFY_FILE ||
            curState.value == OTA_STATE.OTA_STATE_VERIFY_FILE_ERROR ||
          curState.value == OTA_STATE.OTA_STATE_WAIT_VERIFY_DEVICE) {
    return "Verifying update data...";
  }else if (curState.value == OTA_STATE.OTA_STATE_VERIFY_DEVICE ||
            curState.value == OTA_STATE.OTA_STATE_VERIFY_DEVICE_ERROR) {
    return "Validating device...";
  }else if (curState.value == OTA_STATE.OTA_STATE_CHECK_USB ||
            curState.value == OTA_STATE.OTA_STATE_CHECK_USB_ERROR) {
    return "Checking USB status...";
  }else if (curState.value == OTA_STATE.OTA_STATE_READY_TO_UPDATE) {
    return "Ready to update";
  }else if (curState.value == OTA_STATE.OTA_STATE_UPDATE ||
            curState.value == OTA_STATE.OTA_STATE_UPDATE_ERROR) {
    return "Updating...";
  }else if (curState.value == OTA_STATE.OTA_STATE_DONE) {
    return "Update success";
  }else if (curState.value == OTA_STATE.OTA_STATE_NO_UPDATE_FILE) {
    return "No need to update";
  }

  return ""
})

</script>


<template>
  <div class="container" ref="dialogRef">
    <div class="content">
      <!-- Header -->
      <header>
        <div class="header-content">
          <h1>Echo Stone</h1>
          <p style="font-size: 1rem;">Story Cloud AiDoll OTA</p>
        </div>
        <div>
          <span class="iconfont" 
            :class="{ 'icon-usb_conn': isUsbConnected, 'icon-usb_disconn': !isUsbConnected }" 
            :style="{'color': isUsbConnected ? 'green' : 'red', 'font-size': '30px'}"></span>
        </div>
      </header>
      <!-- Error Banner -->
      <div class="error-banner" v-if="showUsbWarning">
        {{ warningText }}
      </div>
      <!-- Input Section -->
      <div class="input-section">
        <div class="input-wrapper">
          <input
            type="text"
            placeholder="OTA Path"
            v-model="otaPath"
            readonly
          />
        </div>
        <button class="action-button rounded-button" @click="getOtaFile" :disabled="isLoading">
          Fetch
        </button>
        <button class="action-button rounded-button" @click="loadOtaFile" :disabled="isLoading">
          Load
        </button>
      </div>
      <!-- Main Content -->
      <div class="main-content">
        <div class="image-container">
          <LoadingIndicator v-if="isLoading" :text="loadingText" />
          <div v-else class="placeholder-content">
            <img src="/logo.png" alt="Placeholder Image" class="scaled-image"/>
            <p class="status-message">
              {{ info_msg }}
            </p>
          </div>
        </div>
        <div v-if="showProgress" class="w-full">
          <ProgressIndicator :progress="progress" />
        </div>
        <button class="continue-button rounded-button" @click="updateBin" :disabled="isLoading || isUpdating">
          {{ isReady ? 'Update' : 'Continue' }}
        </button>
        <div class="debug-checkbox">
          <label class="checkbox-container">
            <input type="checkbox" v-model="debugMode">
            <span class="checkbox-label">Open debug info</span>
          </label>
        </div>
        <!-- Bottom Error Message -->
        <div class="error-message" v-if="errorInfo && debugMode">
          {{ errorInfo }}
        </div>
      </div>
    </div>
  </div>
</template>


<style scoped>
@import url("/iconfont.css");

.container {
  background-color: white;
}

.content {
  width: 600px;
  height: 700px;
  margin: 0 auto;
  padding: 0 0.5rem;
}

header {
  display: flex;
  align-items: center;
  justify-content: space-between;

}

.header-content h1 {
  font-size: 1.5rem;
  font-weight: 600;
  color: #111827;
}

.header-content p {
  color: #4B5563;
}

.error-banner {
  margin-bottom: 0.5rem;
  padding: 0.5rem;
  background-color: #DC2626;
  color: white;
  text-align: center;
  border-radius: 0.25rem;
  max-height: 40px;
}

.input-section {
  margin-bottom: 2rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.input-wrapper {
  position: relative;
  flex: 1;
  margin-right: 1rem;
}

.input-wrapper input {
  width: 100%;
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem;
  border: 1px solid #D1D5DB;
  border-radius: 0.5rem;
}

.action-button {
  background-color: #DC2626;
  color: white;
  padding: 0.5rem 1.5rem;
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
}

.action-button:hover {
  background-color: #B91C1C;
}

.action-button:disabled {
  background-color: #9CA3AF;
  cursor: not-allowed;
}

.rounded-button {
  border-radius: 0.5rem !important;
}

.main-content {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.image-container {
  margin-bottom: 2rem;
  height: 19rem;
  width: 24rem;
  overflow: hidden;
  position: relative;
}

.placeholder-content {
  width: 100%;
  height: 100%;
  background-color: #f3f4f6;
  border-radius: 0.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.status-message {
  position: relative;
  bottom: 1.2rem;
  font-size: 1.125rem;
  font-weight: 500;
  color: #10B981;
}

.continue-button {
  margin-bottom: 1.5rem;
  background-color: #DC2626;
  color: white;
  padding: 0.625rem 2.5rem;
  font-size: 1rem;
  font-weight: 500;
  white-space: nowrap;
}

.continue-button:hover {
  background-color: #B91C1C;
}

.continue-button:disabled {
  background-color: #9CA3AF;
  cursor: not-allowed;
}

.error-message {
  width: 100%;
  padding: 1rem;
  background-color: #DC2626;
  color: white;
  font-size: 0.875rem;
  border-radius: 0.25rem;
  max-height: 60px;
}

.w-full {
  width: 100%;
}
.h-full {
  height: 100%;
}

.scaled-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
</style>
